/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef PLUGINS_ECMASCRIPT_TESTS_RUNTIME_COMMON_TEST_HELPER_H
#define PLUGINS_ECMASCRIPT_TESTS_RUNTIME_COMMON_TEST_HELPER_H

#include "gtest/gtest.h"

#include "runtime/include/method-inl.h"
#include "runtime/include/thread_scopes.h"
#include "plugins/ecmascript/runtime/interpreter/ecma-interpreter.h"
#include "plugins/ecmascript/runtime/interpreter/interpreter.h"
#include "plugins/ecmascript/runtime/ecma_language_context.h"
#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h"
#include "plugins/ecmascript/runtime/ecma_vm.h"
#include "plugins/ecmascript/runtime/object_factory.h"

namespace ark::test {
using ark::ecmascript::EcmaHandleScope;
using ark::ecmascript::EcmaRuntimeCallInfo;
using ark::ecmascript::EcmaVM;
using ark::ecmascript::JSTaggedValue;
using ark::ecmascript::JSThread;

class TestHelper {
public:
    static std::unique_ptr<EcmaRuntimeCallInfo> CreateEcmaRuntimeCallInfo(JSThread *thread, JSTaggedValue newTgt,
                                                                          ArraySizeT argvLength);
    static Frame *SetupFrame(JSThread *thread, [[maybe_unused]] EcmaRuntimeCallInfo *info);
    static void TearDownFrame([[maybe_unused]] JSThread *thread, [[maybe_unused]] Frame *prev);

    static inline void CreateEcmaVMWithScope(PandaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope,
                                             bool enterManagedCode = true)
    {
        RuntimeOptions options;
#if defined(ICU_PATH)
        options.SetIcuDataPath(ICU_PATH);
#endif
        options.SetShouldLoadBootPandaFiles(false);
        options.SetShouldInitializeIntrinsics(false);
        options.SetLoadRuntimes({"ecmascript"});
        options.SetRunGcInPlace(true);
        options.SetExplicitConcurrentGcEnabled(false);
        static EcmaLanguageContext lcEcma;
        [[maybe_unused]] bool success = Runtime::Create(options, {&lcEcma});
        ASSERT_TRUE(success) << "Cannot create Runtime";
        instance = Runtime::GetCurrent()->GetPandaVM();
        ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
        thread = EcmaVM::Cast(instance)->GetAssociatedJSThread();
        scope = new EcmaHandleScope(thread);
        EcmaVM *ecmaVm = thread->GetEcmaVM();
        auto globalEnv = ecmaVm->GetGlobalEnv();
        {
            ScopedManagedCodeThread s(thread);
            methodFunction_ = ecmaVm->GetFactory()->NewJSFunction(globalEnv);
        }
        if (enterManagedCode) {
            thread->ManagedCodeBegin();
        }
    }

    static inline void DestroyEcmaVMWithScope(PandaVM *instance, EcmaHandleScope *scope, bool exitManagedCode = true)
    {
        auto thread = EcmaVM::Cast(instance)->GetAssociatedJSThread();
        if (exitManagedCode) {
            thread->ManagedCodeEnd();
        }
        delete scope;
        EcmaVM::Cast(instance)->GetFactory()->SetTriggerGc(false);
        thread->ClearException();
        [[maybe_unused]] bool success = Runtime::Destroy();
        ASSERT_TRUE(success) << "Cannot destroy Runtime";
    }

private:
    static thread_local bool isLeaf_;
    static ecmascript::JSHandle<ecmascript::JSFunction> methodFunction_;
};
}  // namespace ark::test

#endif  // PLUGINS_ECMASCRIPT_TESTS_RUNTIME_COMMON_TEST_HELPER_H
